package com.xb.loan.encryptor.util;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.security.*;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RSAUtils {

    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    
    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

	private static final String PRIVATE_KEY = "private";

	private static final String PUBLIC_KEY = "public";
	
	private static Log log = LogFactory.getLog(RSAUtils.class);
    
    /**
     * 接入商的公钥或者私钥，第一个cipher用于加密，第二个cipher用于解密
     */
    private static Map<String, Cipher[]> agentCiphers = new ConcurrentHashMap<String, Cipher[]>();
    /**
     * 
     * @param isPrivateKey	公钥或者私钥
     * @param fileName	文件名称
     * @throws Exception
     */
    private static void loadCert(boolean isPrivateKey,String fileName) throws Exception {
    	
    	Cipher[] ciphers = new Cipher[2];
    	
    	byte[] cryptData = Base64Utils.fileToByte(fileName);
    	
    	byte[] data = Base64Utils.decode(new String(cryptData));
    	
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        
        Key key = null;
        
        if(isPrivateKey){
        	
        	PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(data);
        	key = keyFactory.generatePrivate(pkcs8KeySpec);
        } else {
        	
        	X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(data);
            key = keyFactory.generatePublic(x509KeySpec);
        }
        
        Cipher encryptCipher = Cipher.getInstance(keyFactory.getAlgorithm());
        encryptCipher.init(Cipher.ENCRYPT_MODE, key);
        
        Cipher decryptCipher = Cipher.getInstance(keyFactory.getAlgorithm());
        decryptCipher.init(Cipher.DECRYPT_MODE, key);
        
        ciphers[0] = encryptCipher;
        ciphers[1] = decryptCipher;
        
        agentCiphers.put(isPrivateKey? PRIVATE_KEY : PUBLIC_KEY, ciphers);
    }
    // 读取公钥
    public static void loadPubCert(String fileName) throws Exception {
    	
    	loadCert(false, fileName);
    }
    // 读取私钥
    public static void loadPriCert(String fileName) throws Exception {
    	
    	loadCert(true, fileName);
    }
    
    // 使用公钥加密
    public static String encryptByPubK(byte[] data) throws Exception {
    	
    	Cipher cipher = agentCiphers.get(PUBLIC_KEY)[0];
    	
    	byte[] result = null;
    	
    	synchronized (cipher) {
    		
    		result = encryptByCipher(data, cipher);
    		
		}
    	
    	return Base64Utils.encode(result);
    }
    
    // 使用私钥加密
    public static String encryptByPriK(byte[] data) throws Exception {
    	
    	Cipher cipher = agentCiphers.get(PRIVATE_KEY)[0];
    	
    	byte[] result = null;
    	
    	synchronized (cipher) {
    		
    		result = encryptByCipher(data, cipher);
    		
		}
    	
    	return Base64Utils.encode(result);
    }
    
    // 使用公钥解密
    public static byte[] decryptByPubK(String msg) throws Exception {
    	
    	Cipher cipher = agentCiphers.get(PUBLIC_KEY)[1];
    	
    	byte[] result = null;
    	
    	synchronized (cipher) {
    		
    		result = decryptByCipher(Base64Utils.decode(msg), cipher);
    		
		}
    	
    	return result;
    }
    
    // 使用私钥解密
    public static byte[] decryptByPriK(String msg) throws Exception {
    	
    	Cipher cipher = agentCiphers.get(PRIVATE_KEY)[1];
    	
    	byte[] result = null;
    	
    	synchronized (cipher) {
    		
    		result = decryptByCipher(Base64Utils.decode(msg), cipher);
    		
		}
    	
    	return result;
    }
    /**
     * <p>
     * 生成密钥对(公钥和私钥)
     * </p>
     * 
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws Exception
     */
    public static RSAKey[] genKeyPair() throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        
        byte[] data = publicKey.getEncoded();
        
        X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(data);
        
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        
        publicKey = (RSAPublicKey) keyFactory.generatePublic(pubX509);
        
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        
        RSAKey[] keys = new RSAKey[2];
        // 公钥
        keys[0] = publicKey;
        // 私钥
        keys[1] = privateKey;
        
        return keys;
    }
    
    // 生成BASE64加密的公钥和私钥
    public static void genKeyPair(String filePath) throws Exception {
    	
    	RSAKey[] keys = genKeyPair();
    	
    	RSAPublicKey publicKey = (RSAPublicKey) keys[0];
    	
    	RSAPrivateKey privateKey = (RSAPrivateKey) keys[1];
    	
    	Base64Utils.byteArrayToFile(publicKey.getEncoded(), filePath + File.separatorChar + PUBLIC_KEY);
    	Base64Utils.encodeFile(filePath + File.separatorChar + PUBLIC_KEY);
    	
    	Base64Utils.byteArrayToFile(privateKey.getEncoded(), filePath + File.separatorChar + PRIVATE_KEY);
    	Base64Utils.encodeFile(filePath + File.separatorChar + PRIVATE_KEY);
    }
    
    /**
     * <p>
     * 用私钥对信息生成数字签名
     * </p>
     * 
     * @param data 已加密数据
     * @param privateKeyBytes 私钥(字节码)
     * 
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, byte[] privateKeyBytes) throws Exception {
    	
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return Base64Utils.encode(signature.sign());
    }

    /**
     * <p>
     * 校验数字签名
     * </p>
     * 
     * @param data 已加密数据
     * @param publicKeyData 公钥(字节码)
     * @param sign 数字签名
     * 
     * @return
     * @throws Exception
     * 
     */
    public static boolean verify(byte[] data, byte[] publicKeyData, String sign)
            throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyData);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        
        try {
			
        	 return signature.verify(Base64Utils.decode(sign));
		} catch (Exception e) {
			log.error("RSAUtils.verify have exception,sign:" + sign,e);
			
			return false;
		}
        
       
    }

    /**
     * <P>
     * 私钥解密
     * </p>
     * 
     * @param encryptedData 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        return decryptByCipher(encryptedData, cipher);
    }

    // 利用Cipher解密
	public static byte[] decryptByCipher(byte[] encryptedData,
			Cipher cipher) throws Exception {
		int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
	}

    /**
     * <p>
     * 公钥解密
     * </p>
     * 
     * @param encryptedData 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        return decryptByCipher(encryptedData, cipher);
    }

    /**
     * <p>
     * 公钥加密
     * </p>
     * 
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        byte[] encryptedData = encryptByCipher(data, cipher);
        return encryptedData;
    }

    // 利用Cipher加密
	public static byte[] encryptByCipher(byte[] data, Cipher cipher)
			throws IllegalBlockSizeException, BadPaddingException, IOException {
		int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
		return encryptedData;
	}

    /**
     * <p>
     * 私钥加密
     * </p>
     * 
     * @param data 源数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        byte[] encryptedData = encryptByCipher(data, cipher);
        return encryptedData;
    }
    
    public static void main(String[] args) throws Exception {
    	
//    	genKeyPair("D:\\rsaKey");
    	// 加载公钥
    	loadPubCert("F:\\download\\keyPath\\user\\12\\publicKey.txt");
    	// 加载私钥
    	loadPriCert("F:\\download\\keyPath\\user\\12\\privateKey.txt");
    	
    	String msg = "sdsf9932fds";
    	
    	byte[] cryptData = Base64Utils.fileToByte("F:\\download\\keyPath\\user\\12\\privateKey.txt");
    	
    	byte[] data = Base64Utils.decode(new String(cryptData));
    	// 签名
    	String sign = sign(msg.getBytes(), data);
    	
    	System.out.println("sign:" + sign);
    	
    	cryptData = Base64Utils.fileToByte("F:\\download\\keyPath\\user\\12\\publicKey.txt");
    	
    	data = Base64Utils.decode(new String(cryptData));
    	
    	boolean verify = verify(msg.getBytes(), data, sign);
    	
    	System.out.println(verify);
    	
    	// 私钥加密
    	String crypt = encryptByPriK(msg.getBytes());
    	
    	System.out.println(crypt);
    	// 公钥解密
    	data = decryptByPubK(crypt);

    	System.out.println(new String(data));
    	// 公钥加密
    	crypt = encryptByPubK(msg.getBytes());
    	
    	System.out.println(crypt);
    	// 私钥解密
    	data = decryptByPriK(crypt);
    	
    	System.out.println(new String(data));
    	
	}
    
}